
package com.mephi.controller;


import java.util.Calendar;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Vector;
import com.mephi.interfaces.Game;
import com.mephi.interfaces.GameIterator;
import com.mephi.interfaces.Position;
import com.mephi.model.Move;
import com.mephi.model.PieceMovement;
import com.mephi.util.AuxClass;
import com.mephi.util.PieceColor;


/**
 * This class represents the chess game
 * @author mephisto
 * @since 2011-04-22
 *
 */
public class GameImpl<E extends Move> implements Game<E>
{
	public GameImpl()
	{
		this(DEFAULT_GAME_SIZE);	
	}
	
	/**
	 * 
	 * @param moveNum the moveNum to set
	 */
	public GameImpl(int moveNum)
	{
		movesList = new Vector<E>(moveNum);
		it = movesList.listIterator();
	}
	
	/**
	 * @param position the starting position to set
	 * @param movesList the movesList to set 
	 * @param white the white to set 
	 * @param black the black to set 
	 * @param result the result to set 
	 */
	public GameImpl(Position position, List<E> movesList, String white, String black,
			GameResult result)
	{
		setPosition(position);
		setMovesList(movesList);
		setWhite(white);
		setBlack(black);
		setResult(result);
		it = movesList.listIterator();
	}

	@Override
	public void add(E move)
	{
		movesList.add(move);	
	}

	@Override
	public boolean hasNext()
	{
		return it.hasNext();
	}

	@Override
	public boolean hasPrevious()
	{
		return it.hasPrevious();
	}

	@Override
	public E next()
	{
		return it.next();
	}

	@Override
	public int nextIndex()
	{
		return it.nextIndex();
	}

	@Override
	public E previous()
	{
		return it.previous();
	}

	@Override
	public int previousIndex()
	{
		return it.previousIndex();
	}

	@Override
	public void remove()
	{
		 it.remove();	
	}

	@Override
	public void set(E move)
	{
		it.set(move);	
	}

	@Override
	public void clear()
	{
		movesList.clear();
	}

	@Override
	public boolean contains(Object o)
	{
		return movesList.contains(o);
	}

	@Override
	public E get(int index)
	{
		return (E) movesList.get(index);
	}

	@Override
	public E set(int index, E move)
	{
		return movesList.set(index, move);
	}

	@Override
	public int size()
	{
		return movesList.size();
	}
	
	/**
	 * @return the movesList
	 */
	public List<E> getMovesList()
	{
		return movesList;
	}

	/**
	 * @param movesList the movesList to set
	 * It makes old iterator invalid!
	 */
	@Override
	public void setMovesList(List<E> movesList)
	{
		this.movesList = movesList;
		it = movesList.listIterator();
	}

	/**
	 * @return the event
	 */
	public String getEvent()
	{
		return event;
	}

	/**
	 * @param event the event to set
	 */
	public void setEvent(String event)
	{
		this.event = event;
	}

	/**
	 * @return the site
	 */
	public String getSite()
	{
		return site;
	}

	/**
	 * @param site the site to set
	 */
	public void setSite(String site)
	{
		this.site = site;
	}

	/**
	 * @return the round
	 */
	public int getRound()
	{
		return round;
	}

	/**
	 * @param round the round to set
	 */
	public void setRound(int round)
	{
		this.round = round;
	}

	/**
	 * @return the white
	 */
	public String getWhite()
	{
		return white;
	}

	/**
	 * @param white the white to set
	 */
	public void setWhite(String white)
	{
		this.white = white;
	}

	/**
	 * @return the black
	 */
	public String getBlack()
	{
		return black;
	}

	/**
	 * @param black the black to set
	 */
	public void setBlack(String black)
	{
		this.black = black;
	}

	/**
	 * @return the result
	 */
	public GameResult getResult()
	{
		return result;
	}

	/**
	 * @param result the result to set
	 */
	public void setResult(GameResult result)
	{
		this.result = result;
	}

	/**
	 * @return the timeControl
	 */
	public String getTimeControl()
	{
		return timeControl;
	}

	/**
	 * @param timeControl the timeControl to set
	 */
	public void setTimeControl(String timeControl)
	{
		this.timeControl = timeControl;
	}

	/**
	 * @return the whiteElo
	 */
	public int getWhiteElo()
	{
		return whiteElo;
	}

	/**
	 * @param whiteElo the whiteElo to set
	 */
	public void setWhiteElo(int whiteElo)
	{
		this.whiteElo = whiteElo;
	}

	/**
	 * @return the blackElo
	 */
	public int getBlackElo()
	{
		return blackElo;
	}

	/**
	 * @param blackElo the blackElo to set
	 */
	public void setBlackElo(int blackElo)
	{
		this.blackElo = blackElo;
	}

	/**
	 * @return the calendar
	 */
	public Calendar getCalendar()
	{
		return calendar;
	}

	/**
	 * @param calendar the calendar to set
	 */
	public void setCalendar(Calendar calendar)
	{
		this.calendar = calendar;
	}
	
	@Override
	public int getId()
	{
		return id;
	}

	@Override
	public void setId(int id)
	{
		this.id = id; 
	}
	
	/**
	 * 
	 * @param position the position of chess pieces to set
	 * 
	 * @throws NullPointerException if position given as a parameter is null
	 */
	@Override
	public void setPosition(Position position)
	{
		AuxClass.checkArgument(position, new NullPointerException("position cannot be null!"));
		this.position = position;
	}

	/**
	 * 
	 * @return the position of the chess pieces from which this game must be started 
	 */
	@Override
	public Position getPosition()
	{
		return position;
	}
	
	@Override
	public Iterator<E> iterator()
	{
		return movesList.iterator();
	}
	
	@Override
	public GameIterator gameIterator()
	{
		return new GameIteratorImpl();
	}
	
	public String toString()
	{
		return AuxClass.toString(this, 1000);
	}


	private static final int DEFAULT_GAME_SIZE=40;
	
	private int id;
	private String event;
	private String site;
	//date and time
	private Calendar calendar;
	private int round;
	private String white;
	private String black;
	private GameResult result;
	private String timeControl;
	private int whiteElo;
	private int blackElo;
	private List<E> movesList;
	private ListIterator<E> it;
	private Position position;

	
	private class GameIteratorImpl implements GameIterator
	{
		PieceColor pieceColor;
		int cursor;
		int lastRet;
		//int expectedModCount;
	
		private GameIteratorImpl()
		{	
			this.lastRet = -1;
			pieceColor = PieceColor.WHITE;
			//this.expectedModCount = ArrayList.this.modCount; 
		}
		
		private boolean isPieceMovement(int number, PieceColor pieceColor)
		{
			/*Move m = GameImpl.this.movesList.get(number);
			String pieceMove = null;
			if (pieceColor == PieceColor.WHITE)
				pieceMove = m.getWhiteMove();
			else
				pieceMove = m.getBlackMove();
			
			if(Pattern.matches(RESULT_EXPRESSION, pieceMove))
					return false;*/
			return true;
		}
	
	    public boolean hasNextPieceMovement() 
	    {
	    	PieceColor pC = null;
	    	int size = GameImpl.this.movesList.size(); 
	    	if(pieceColor == PieceColor.WHITE)
	    		pC = PieceColor.BLACK;
	    	else
	    		pC = PieceColor.WHITE;
	    	
		    return (this.cursor != size &&
		    		isPieceMovement(size-1,  pC));
	    }
	    
	    public PieceMovement nextPieceMovement()
	    {
	    	checkForComodification();
	        int i = this.cursor;
		    if (!hasNextPieceMovement())
		    	throw new NoSuchElementException();
		    Move m = GameImpl.this.movesList.get(i);
		    String pieceMove = null;
			if (pieceColor == PieceColor.WHITE)
				pieceMove = m.getWhiteMove();
			else
				pieceMove = m.getBlackMove();

			PieceMovement pM =  new PieceMovement(
					this.lastRet = i, pieceMove, pieceColor);
			
			if (pieceColor == PieceColor.WHITE)
				pieceColor = PieceColor.BLACK;
			else
			{
				pieceColor = PieceColor.WHITE;
				this.cursor = (i + 1);
			}
				
				
	       return pM;
	   }
		 
	   public void removePieceMovement() 
	   {
		   if (this.lastRet < 0)
			   throw new IllegalStateException();
		   checkForComodification();
		   try
		   {
			   GameImpl.this.movesList.remove(this.lastRet);
			   this.cursor = this.lastRet;
			   this.lastRet = -1;
		   }
		   catch (IndexOutOfBoundsException ex)
		   {
			   throw new ConcurrentModificationException();
		   }
	   }
		
		   final void checkForComodification() 
		   {
		      /*if (ArrayList.this.modCount != this.expectedModCount)
		        throw new ConcurrentModificationException();
		     }*/
		  }
		   
	}

	private static final String RESULT_EXPRESSION = "[1-0]|[0-1]|[0.5-0.5]";
}
